#include "stdafx.h"
#include "kernel.h"
#include "proc.h"
#include "view.h"

namespace xm
{
	struct ConstAddress
	{
		static vxvma_t	UserTop(uint32_t opt) { return (opt&PROC_64Bit)?PROC64_USER_TOP:PROC32_USER_TOP; }
		static vxvma_t	UserShareData(uint32_t opt) { return (opt&PROC_64Bit)?PROC64_USER_TOP:PROC32_USER_TOP; }
		static vxvma_t	MiniCoreBase(uint32_t opt) { return (opt&PROC_64Bit)?MINI_CORE_BASE64:MINI_CORE_BASE32; }
	};

	
	ghptr::~ghptr()
	{
		destroy();
	}
	bool_t ghptr::alloc( size_t size )
	{
		long lr = proc.heap_alloc(&ptr,0,0,size);
		if( lr < 0 ) return false;
		return true;
	}
	bool_t ghptr::write( size_t off, const void * p, size_t cb )
	{
		if( !ptr ) return false;
		size_t wc = cb;
		long lr = proc.vms_write( ptr + off, p, &wc, 0 );
		if( lr < 0 || wc != cb ) return false;
		return true;
	}
	bool_t ghptr::read( size_t off, void * p, size_t cb )
	{
		if( !ptr ) return false;
		size_t rc = cb;
		long lr = proc.vms_read( ptr + off, p, &rc, 0 );
		if( lr < 0 || rc != cb ) return false;
		return true;
	}
	bool_t ghptr::destroy()
	{
		if( !ptr ) return true;
		long lr = proc.heap_free( 0, ptr );
		return lr == S_OK;
	}
	vxvma_t ghptr::detach()
	{
		vxvma_t ret = ptr;
		ptr = 0;
		return ret;
	}

	bool_t ghptr::abwrite( vxvma_t va, const void * p, size_t cb )
	{
		size_t wc = cb;
		long lr = proc.vms_write( va, p, &wc, 0 );
		if( lr < 0 || wc != cb ) return false;
		return true;
	}

	//////////////////////////////////////////////////////////////////////////

	
	template < class T >
	bool_t gstruct<T>::flush()
	{
		bool_t fok = guest.write( 0, &t, sizeof(t) );
		if( fok ) guest.detach();
		return fok;
	}

	//////////////////////////////////////////////////////////////////////////

	process_t::process_t( kernel_t& k_ ) 
		: k(k_)
		, peb(0)
		, pid(0)
		, opts(0)
		, heap(0)
		, mainbase(0)
	{

	}
	long process_t::init( process_t * father, uint32_t opt )
	{
		//////////////////////////////////////////////////////////////////////////
		//
		// Init User Space
		//

		vxvma_t room = ConstAddress::UserTop(opt);
		RFAILED( vms.init( room ) );

		//////////////////////////////////////////////////////////////////////////
		//
		// Init GuestCall thread
		//

		giret =   room + 0x80000 + rand()%0x40000;
		gunkapi = room + 0xC0000 + rand()%0x40000;

		//////////////////////////////////////////////////////////////////////////
		//
		// Reverse PAGE
		//

		vms.alloc( 0, 0x10000, PGA_REVERSE, 0x10000 );
		vms.alloc( room - 0x10000, 0x10000, PGA_REVERSE, 0x10000 );

		//////////////////////////////////////////////////////////////////////////
		//
		//	FIXME: map KUSER_SHARED_DATA section
		//

		section_map_file( k.ntk.shared_data_section(), 0, VM_PAGE_SIZE, FILE_MAP_READ|FILE_MAP_COPY, 0x7FFE0000 );
		//////////////////////////////////////////////////////////////////////////
		//
		// 

		//////////////////////////////////////////////////////////////////////////
		//
		//	init PID
		//

		pid = k.client_id_alloc();

		//////////////////////////////////////////////////////////////////////////
		//
		// Map minicore
		//

		vmarea_t * area = section_map_image( k.ntk.mc_section(0), ~(IMG_MAP_PIN_MOD|IMG_MAP_RELOCATE|IMG_MAP_IMPORT) );
		if( !area ) return E_FAIL;

		//////////////////////////////////////////////////////////////////////////
		//
		// ALLOC PEB
		//

		vmarea_t * peb_area = vms.alloc( 0, 0x1000, PGA_READ|PGA_WRITE|PGA_COMMIT|PGA_TOP_DOWN, 0x1000 );
		if( !peb_area ) return E_FAIL;
		peb = peb_area->base;

		//////////////////////////////////////////////////////////////////////////
		//
		// Init PEB
		//
		
		rmb_t * peb_rmb = _page_get_to_write( peb_area, peb, false );
		if( !peb_rmb ) return E_FAIL;

		//////////////////////////////////////////////////////////////////////////
		{
			if( opt & PROC_64Bit )
			{
				PEB64* pebp = (PEB64*)rmb_lock(peb_rmb);
				if( !pebp ) return E_FAIL;
				k.process_init_peb64( pebp );
			}
			else
			{
				PEB32* pebp = (PEB32*)rmb_lock(peb_rmb);
				if( !pebp ) return E_FAIL;
				k.process_init_peb32( pebp );
			}
		}
		rmb_unlock(peb_rmb);

		//////////////////////////////////////////////////////////////////////////
		//
		// Init GuestCall thread
		//
		const size_t GITO_STACK_SIZE = 0xF000;
		gito = k.thread_create( this, 0, GITO_STACK_SIZE, ~TOPT_USER_THREAD, ConstAddress::MiniCoreBase(opt) - 0x10000 );
		if( !gito ) return E_OUTOFMEMORY;

		//////////////////////////////////////////////////////////////////////////
		//
		// init CR3
		//

		RFAILED( pgt.init() );

		parent = father;
		opts = opt;

		return S_OK;
	}

	//////////////////////////////////////////////////////////////////////////

	long process_t::init_rup()
	{
		return 0;
	}
	//////////////////////////////////////////////////////////////////////////
	long process_t::module_enum( )
	{
		return 0;
	}
	//////////////////////////////////////////////////////////////////////////
	vxvma_t process_t::heap_get_def()
	{
		vxvma_t ret;
		gito->rewind();
		RFAILED_( gito->ginvoker( &ret, mc_func(McGetProcessHeap), GI_HOSTCALL ), 0 );
		return ret;
	}
	long process_t::heap_create( vxvma_t * hheap, size_t init_size, size_t max_size, uint32_t flags )
	{
		// HANDLE	WINAPI	McCreateHeap( ULONG flags, PVOID addr, SIZE_T totalSize, SIZE_T commitSize );
		gito->rewind();
		RFAILED( gito->push( init_size ) );
		RFAILED( gito->push( max_size ) );
		RFAILED( gito->push( 0 ) );
		RFAILED( gito->push( flags ) );
		RFAILED( gito->ginvoker( hheap, mc_func(McCreateHeap), GI_HOSTCALL ) );
		return S_OK;
	}
	long process_t::heap_alloc( vxvma_t * addr, vxvma_t hheap, uint32_t flags, size_t want )
	{
		// PVOID WINAPI McAllocateHeap( HANDLE heap, ULONG flags, SIZE_T size )
		gito->rewind();
		RFAILED( gito->push( want ) );
		RFAILED( gito->push( flags ) );
		RFAILED( gito->push( hheap ) );
		RFAILED( gito->ginvoker( addr, mc_func(McAllocateHeap), GI_HOSTCALL ) );
		return S_OK;
	}
	long process_t::heap_free( vxvma_t hheap, vxvma_t addr )
	{
		gito->rewind();
		RFAILED( gito->push( addr ) );
		RFAILED( gito->push( hheap ) );
		RFAILED( gito->ginvoke( mc_func(McFreeHeap), GI_HOSTCALL ) );
		return S_OK;
	}
	long process_t::heap_destroy( vxvma_t hheap )
	{
		gito->rewind();
		RFAILED( gito->push( hheap ) );
		RFAILED( gito->ginvoke( mc_func(McDestroyHeap), GI_HOSTCALL ) );
		return E_FAIL;
	}
	long process_t::heap_enum( size_t idx, vxvma_t * heaps, size_t want, size_t * rest )
	{
		return E_FAIL;
	}
	
	long process_t::ldr_insert( vxvma_t peb, vxvma_t ldrmod, vxvma_t mem_prev )
	{
		vxlong_t lr;
		gito->rewind();
		RFAILED( gito->push( mem_prev ) );
		RFAILED( gito->push( ldrmod ) );
		RFAILED( gito->push( peb ) );
		RFAILED( gito->ginvoker( &lr, mc_func(McLdrInitOrInsertModule), GI_HOSTCALL ) );
		return (long)lr;
	}

	vxvma_t process_t::mc_func( McFunc id )
	{
		BOOL is64 = opts & PROC_64Bit;
		vxvma_t rva = k.ntk.mc_image(is64)->get_proc_address_by_ord(id);
		if( !rva ) return 0;
		return rva + (is64 ? MINI_CORE_BASE64 : MINI_CORE_BASE32); 
	}
	vxvma_t process_t::mc_func( const char * name )
	{
		BOOL is64 = opts & PROC_64Bit;
		uint32_t rva = k.ntk.mc_image(is64)->get_proc_address_by_name(name);
		if( !rva ) return 0;
		return rva + (is64 ? MINI_CORE_BASE64 : MINI_CORE_BASE32); 
	}

	long process_t::install()
	{
		if( !pgt.load() ) return E_FAIL;
		bps_t::iterator it = bps.begin();
		for( ; it != bps.end(); ++ it )
			vx_set_bp( it->type, it->addr, it->siz );
		return S_OK;
	}

	long process_t::uninstall()
	{
		vx64_remove_all_bps();
		vx64_remove_all_wps();
		if( !pgt.unload() )
			return E_FAIL;
		return S_OK;
	}

	bool_t process_t::is_current_process()
	{
		if( !k.current_thread() ) return false;
		return k.current_thread()->proc == this;
	}

	long process_t::set_bp( bptype_t type, vxvma_t addr, size_t siz /*= 1 */ )
	{
		vxbp_t bp = { type, addr, siz };
		if( type != VX_BPX && !siz ) return E_FAIL;

		bps_t::_Pairib ib = bps.insert( bp );
		if( !ib.second ) return E_FAIL;

		if( is_current_process() ) 
			vx_set_bp( type, addr, siz );

		return S_OK;
	}

	void process_t::vx_set_bp( bptype_t type, vxvma_t addr, vxlong_t siz )
	{
		if( type == VX_BPX )
		{
			vx64_insert_bp( addr );
		}
		else if( type == VX_BPW )
		{
			vx64_insert_wp( addr, siz, VXWP_WRITE );
		}
		else if( type == VX_BPR )
		{
			vx64_insert_wp( addr, siz, VXWP_READ );
		}
	}

	optr<process_t> process_t::ez_create( 
		kernel_t& k, 
		const char * imgfile, 
		const char * cmdline, 
		size_t stackSize, 
		size_t opts,
		process_t * parent )
	{
		optr<section_t> sect = new section_t();
		RFAILED_( sect->create_image_section_w32( k, imgfile ), NULL );
		optr<imagex_t> imgo = sect->get_image();
		if( !imgo ) return NULL;
		size_t opts2 = imgo->_is64 ? PROC_64Bit : 0;
		optr<process_t> proc = k.process_create( parent, opts2 | opts );
		if( !proc ) return NULL;
		vmarea_t * ldbase = 0;
		RFAILED_( proc->image_load( &ldbase, sect ), NULL );
		proc->mainbase = ldbase->base;
		vxlong_t ep = imgo->entry_point() + ldbase->base;
		//////////////////////////////////////////////////////////////////////////
		optr<thread_t> tho = k.thread_create( proc, ep, stackSize );
		if( !tho ) return NULL;
		return proc;
	}

	long process_t::on_thread_attached( thread_t * tho )
	{
		if( !mainthread ) 
		{
			mainthread = tho;
			return S_OK;
		}
		// call all thread 
		size_t mc = modmgr.get_module_count();
		std::vector< optr<image_view_t> > views;
		views.reserve( mc );
		modmgr.fetch_modules( views, mod_mgr_t::COND_HAS_ENTRY|mod_mgr_t::COND_HAS_TLS );
		mc = views.size();
		for( size_t i = 0; i < mc; ++ i )
		{
		}
		return 0;
	}
	long process_t::on_thread_detached( thread_t * tho )
	{
		return 0;
	}
};